﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Collections;

namespace SimpleContainer
{
    
    /// <summary>
    /// Synchronized collection of keys and values.
    /// </summary>
    /// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam>
    /// <typeparam name="TValue">The type of the values in the dictionary.</typeparam>
    public class SynchronizedDictionary<TKey, TValue> : IDictionary<TKey, TValue>
    {

        /// <summary>
        /// Read-write synchronization.
        /// </summary>
        public readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);


        /// <summary>
        /// Wrapped dictionary.
        /// </summary>
        protected Dictionary<TKey, TValue> _Dictionary;

        public SynchronizedDictionary()
        {
            this._Dictionary = new Dictionary<TKey, TValue>();
        }

        public SynchronizedDictionary(IEqualityComparer<TKey> comparer)
        {
            this._Dictionary = new Dictionary<TKey, TValue>(comparer);
        }

        public SynchronizedDictionary(int capacity)
        {
            this._Dictionary = new Dictionary<TKey, TValue>(capacity);
        }


        #region IDictionary members

        public void Add(KeyValuePair<TKey, TValue> item)
        {
            using (new AcquireWriteLock(this.Lock))
            {
                this._Dictionary[item.Key] = item.Value;
            }
        }

        public void Add(TKey key, TValue value)
        {
            using (new AcquireWriteLock(this.Lock))
            {
                this._Dictionary[key] = value;
            }
        }

        public void Clear()
        {
            using (new AcquireWriteLock(this.Lock))
            {
                this._Dictionary.Clear();
            }
        }

        public bool Contains(KeyValuePair<TKey, TValue> item)
        {
            using (new AcquireReadLock(this.Lock))
            {
                return this._Dictionary.Contains(item);
            }
        }

        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            using (new AcquireReadLock(this.Lock))
            {
                this._Dictionary.ToArray().CopyTo(array, arrayIndex);
            }
        }

        public int Count
        {
            get
            {
                using (new AcquireReadLock(this.Lock))
                {
                    return this._Dictionary.Count;
                }
            }
        }

        public bool IsReadOnly
        {
            get { return false; }
        }

        public bool Remove(KeyValuePair<TKey, TValue> item)
        {
            using (new AcquireWriteLock(this.Lock))
            {
                return this._Dictionary.Remove(item.Key);
            }
        }

        public bool ContainsKey(TKey key)
        {
            using (new AcquireReadLock(this.Lock))
            {
                return this._Dictionary.ContainsKey(key);
            }
        }

        public TValue this[TKey key]
        {
            get
            {
                using (new AcquireReadLock(this.Lock))
                {
                    return this._Dictionary[key];
                }
            }
            set
            {
                using (new AcquireWriteLock(this.Lock))
                {
                    this._Dictionary[key] = value;
                }
            }
        }

        public ICollection<TKey> Keys
        {
            get
            {
                using (new AcquireReadLock(this.Lock))
                {
                    return this._Dictionary.Keys;
                }
            }
        }

        public bool Remove(TKey key)
        {
            using (new AcquireWriteLock(this.Lock))
            {
                return this._Dictionary.Remove(key);
            }
        }

        public bool TryGetValue(TKey key, out TValue value)
        {
            using (new AcquireReadLock(this.Lock))
            {
                return this._Dictionary.TryGetValue(key, out value);
            }
        }

        public ICollection<TValue> Values
        {
            get
            {
                using (new AcquireReadLock(this.Lock))
                {
                    return this._Dictionary.Values;
                }
            }
        }

        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumeratorGeneric()
        {
            using (new AcquireReadLock(this.Lock))
            {
                return this._Dictionary.GetEnumerator();
            }
        }

        IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
        {
            return GetEnumeratorGeneric();
        }

        public IEnumerator GetEnumerator()
        {
            using (new AcquireReadLock(this.Lock))
            {
                return this._Dictionary.GetEnumerator();
            }
        }

        #endregion


        #region public TValue GetOrAdd(TKey key, Func<TKey, TValue> getValue)
        /// <summary>
        /// Tries to get value by key. If key is not found the adds new value that is got from specified delegate.
        /// </summary>
        /// <param name="key">Key.</param>
        /// <param name="getValue">Delegate for value obtaining. If null then default value is used.</param>
        /// <returns>If specified key has been present then returns coresponding value. Otherwise returns value that has got from specified delegate.</returns>
        public TValue GetOrAdd(TKey key, Func<TKey, TValue> getValue)
        {
            using (new AcquireReadLock(this.Lock))
            {
                TValue res;
                if (_Dictionary.TryGetValue(key, out res))
                {
                    return res;
                }
            }

            using (new AcquireUpgradeableReadLock(this.Lock))
            {
                TValue res;
                if (_Dictionary.TryGetValue(key, out res))
                {
                    return res;
                }
                using(new AcquireWriteLock(this.Lock))
                {
                    res = getValue != null ? getValue(key) : default(TValue);
                    _Dictionary.Add(key, res);
                    return res;
                }
            }
            
            //using (new AcquireWriteLock(this.Lock))
            //{
            //    TValue res;
            //    if (_Dictionary.TryGetValue(key, out res))
            //    {
            //        return res;
            //    }
            //    res = getValue != null ? getValue(key) : default(TValue);
            //    _Dictionary.Add(key, res);
            //    return res;
            //}
        }
        #endregion

    }

}
